import { cloneDeep } from 'lodash-es'
import { BwReplaceFieldNames } from './model'

const replaceFields = {
  id: 'id',
  name: 'name',
  children: 'children',
  parentId: 'parentId',
}

export const bwUtilsArray = {
  /**
   * 数组添加元素
   * @data                          目标数组
   * @ele                           目标元素
   * @index                         插入位置
   */
  add: (data: Array<any>, ele: any, index: number) => {
    if (Array.isArray(data)) {
      index = [undefined, null].includes(index as any) ? -1 : index
      if (index < 0) {
        data.push(ele)
      } else if (index === 0) {
        data.unshift(ele)
      } else {
        data.splice(index, 0, ele)
      }
    }
    return data
  },

  /**
   * 数组扁平化
   * @data                              目标数据
   * @fields                            替换字段
   * @pId                               父级Id
   * @callback                          回调方法
   */
  flatArray(
    data: Array<Record<string, any>>,
    fields: BwReplaceFieldNames,
    pId?: string | number | null,
    callback?: (
      item: Record<string, any>
    ) => Record<string, any> & { $parent_id: string }
  ) {
    const list: any[] = []
    if (Array.isArray(data) && data.length) {
      const { id, children } = Object.assign(replaceFields, fields)

      data.forEach((item: any) => {
        const itemObj = cloneDeep(item)
        const childData = itemObj[children]

        itemObj.$parent_id = pId
        typeof callback === 'function'
          ? callback(itemObj) && list.push(itemObj)
          : list.push(itemObj)

        delete itemObj[children]
        if (Array.isArray(childData) && childData.length) {
          const childList = bwUtilsArray.flatArray(
            childData,
            fields,
            itemObj[id],
            callback
          )
          list.push(...childList)
        }
      })
    }
    return list
  },

  /**
   * 列表转树数据
   * @data                              目标数据
   * @fields                            替换字段
   * @pId                               父级Id
   * @callback                          回调方法
   * @level                             层级
   */
  toTreeArray(
    data: Array<Record<string, any>>,
    fields?: BwReplaceFieldNames,
    pId?: number | string,
    callback?: (
      item: Record<string, any>,
      curLevel?: number
    ) => Record<string, any>,
    level?: number
  ): any[] {
    const tree: any[] = []

    if (Array.isArray(data) && data.length) {
      const topPIds = ['', '0', 0, null, undefined]
      const { id: iField, parentId: pField } = Object.assign(
        replaceFields,
        fields || {}
      )

      const list: any[] = data.filter((n) => {
        const itemPId = n[pField]
        return pId ? itemPId == pId : topPIds.includes(itemPId)
      })

      if (Array.isArray(list) && list.length) {
        list.forEach((item: any) => {
          let childItems: any[] = []
          const curLevel = level || 0
          const curPId = item[iField]

          if (!topPIds.includes(curPId)) {
            childItems = bwUtilsArray.toTreeArray(
              data,
              fields,
              curPId,
              callback,
              curLevel + 1
            )
          }

          if (typeof callback === 'function') {
            const data = callback(item, curLevel)
            tree.push(Object.assign(data, { children: childItems }))
          } else {
            item.$level = curLevel
            item.children = childItems
          }
        })
      }
    }
    return tree
  },

  /**
   * 根据唯一字段获取树数组元素
   * @data                              目标数据
   * @value                             唯一字段值
   * @fields                            替换字段
   */
  findTree(
    data: Array<Record<string, any>>,
    value: number | string,
    fields?: BwReplaceFieldNames
  ) {
    if (Array.isArray(data) && data.length) {
      let item: any = null
      const { id, children } = fields || {}
      data.some((n) => {
        item =
          n[id || 'id'] == value
            ? n
            : bwUtilsArray.findTree(n[children || 'children'], value, fields)
        return !!item
      })
      return item
    }
  },

  /**
   * @param nodes 要过滤的树节点集（多根）
   * @param predicate 过滤条件，返回 `true` 保留
   * @returns 过滤后的树节点集
   */
  filterTree(
    nodes: Array<Record<string, any>>,
    predicate: (node: Record<string, any>) => boolean
  ): Array<Record<string, any>> | undefined {
    if (!nodes?.length) {
      return nodes
    }

    // 直接使用 Array 的 filter 可以过滤当层节点
    return nodes.filter((it) => {
      // 不符合条件的直接砍掉
      if (!predicate(it)) {
        return false
      }

      // 符合条件的保留，并且需要递归处理其子节点
      it.children = bwUtilsArray.filterTree(it.children, predicate)
      return true
    })
  },

  /**
   * 过滤列表数组
   * @data                          目标数组
   * @field                         目标数组
   * @value                         目标数组
   * @negate                        取反，true 时去"否"值，false时取"是"值
   */
  filterList(
    data: Array<Record<string, any>>,
    value: number | string,
    field: string,
    negate?: boolean
  ) {
    field = field || 'id'
    if (Array.isArray(data)) {
      return data.filter((m: any) => {
        return negate ? !(m[field] === value) : m[field] === value
      })
    }
    return data
  },

  findTreeNode<T = any>(
    tree: any,
    func: (...args: any[]) => any,
    config?: BwReplaceFieldNames
  ): T | null {
    config = Object.assign(replaceFields, config || {})

    const { children } = config
    const list = [...tree]
    for (const node of list) {
      if (func(node)) return node
      node[children!] && list.push(...node[children!])
    }
    return null
  },
}
